/**
 * Copyright (C) 2010 Cloudfarming <info@cloudfarming.nl>
 *
 * Licensed under the Eclipse Public License - v 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.eclipse.org/legal/epl-v10.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package nl.cloudfarming.client.geoviewer.jxmap.render;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import nl.cloudfarming.client.geoviewer.Palette;
import nl.cloudfarming.client.geoviewer.ValueRange;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.ViewType;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.filter.expression.ExpressionAbstract;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.renderer.lite.gridcoverage2d.GridCoverageRenderer;
import org.geotools.styling.ColorMap;
import org.geotools.styling.ColorMapEntry;
import org.geotools.styling.ColorMapEntryImpl;
import org.geotools.styling.RasterSymbolizer;
import org.geotools.styling.StyleFactory;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.mapviewer.GeoPosition;
import org.opengis.filter.expression.ExpressionVisitor;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.openide.util.Exceptions;

/**
 * GridCoverage2DRenderer
 *
 *
 * @author Timon Veenstra
 */
public class GridCoverage2DRenderer implements RasterRenderer<GridCoverage2D> {

    /**
     * this gets rid of exception for not using native acceleration
     */
    static {
        System.setProperty("com.sun.media.jai.disableMediaLib", "true");
    }
    private StyleFactory sf = CommonFactoryFinder.getStyleFactory(null);

    @Override
    public void paint(GridCoverage2D raster, JXMapViewer mapViewer, Graphics2D graphics, Palette palette) {
        // skip painting is raster is empty
        if (raster == null) {
            return;
        }

        try {
            double minx = raster.getEnvelope2D().getMinX();
            double maxx = raster.getEnvelope2D().getMaxX();
            double miny = raster.getEnvelope2D().getMinY();
            double maxy = raster.getEnvelope2D().getMaxY();


            final Point2D upLeft = mapViewer.getTileFactory().geoToPixel(new GeoPosition(miny, minx), mapViewer.getZoom());
            final Point2D downRight = mapViewer.getTileFactory().geoToPixel(new GeoPosition(maxy, maxx), mapViewer.getZoom());

            Rectangle bounds = new Rectangle(0, 0, -1, -1);

            int x = (int) (upLeft.getX() - mapViewer.getViewportBounds().getX());
            int y = (int) (upLeft.getY() - mapViewer.getViewportBounds().getY());

            bounds.setLocation(x, y);

            x = (int) (upLeft.getX() - mapViewer.getViewportBounds().getX());
            y = (int) (downRight.getY() - mapViewer.getViewportBounds().getY());

            bounds.add(x, y);

            x = (int) (downRight.getX() - mapViewer.getViewportBounds().getX());
            y = (int) (downRight.getY() - mapViewer.getViewportBounds().getY());

            bounds.add(x, y);

            x = (int) (downRight.getX() - mapViewer.getViewportBounds().getX());
            y = (int) (upLeft.getY() - mapViewer.getViewportBounds().getY());

            bounds.add(x, y);


            com.vividsolutions.jts.geom.Envelope rasterEnvelope = new com.vividsolutions.jts.geom.Envelope(minx, maxx, miny, maxy);
            GridCoverageRenderer renderer = new GridCoverageRenderer(DefaultGeographicCRS.WGS84, rasterEnvelope, bounds, (AffineTransform) null);
            RasterSymbolizer symbolizer = sf.getDefaultRasterSymbolizer();
            symbolizer.setOpacity(new OpacityExp(0.9f));

            if (palette instanceof ValueRange) {
                ValueRange range = (ValueRange)palette;
                Number min = range.getMinValue();
                Number max = range.getMaxValue();
                double step = (max.doubleValue()-min.doubleValue())/range.getSteps();
                for (int s=0;s<range.getSteps();s++){
                    Integer value = new Double(min.doubleValue()+(s*step)).intValue();
                    Color c = palette.getColorForValue(value);
                    symbolizer.getColorMap().addColorMapEntry(getEntry(c,value.intValue()));
                }

            } else {
                //
                //fallback
                //
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFF000000, true), 1));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFF8F050C, true), 100));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFFDB0F01, true), 200));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFFE65E00, true), 300));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFFFC9F00, true), 400));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFFFAD007, true), 500));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFFDCF40A, true), 600));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFF92FC00, true), 700));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFF41C405, true), 800));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFF048608, true), 900));
                symbolizer.getColorMap().addColorMapEntry(getEntry(new Color(0xFF044606, true), 1000));
            }
            symbolizer.getColorMap().setType(ColorMap.TYPE_RAMP);

            renderer.paint(graphics, raster.view(ViewType.GEOPHYSICS), symbolizer);
        } catch (FactoryException ex) {
            Exceptions.printStackTrace(ex);
        } catch (TransformException ex) {
            Exceptions.printStackTrace(ex);
        } catch (NoninvertibleTransformException ex) {
            Exceptions.printStackTrace(ex);
        }
    }

    private ColorMapEntry getEntry(Color color, int value) {
        ColorMapEntry entry = new ColorMapEntryImpl();
        entry.setColor(new ColorExp(color));
        entry.setQuantity(new ValueExp(value));
        return entry;
    }

    private class ColorExp extends ExpressionAbstract {

        private final Color color;

        public ColorExp(Color color) {
            this.color = color;
        }

        @Override
        public Object evaluate(Object object) {
            return this.color;
        }

        @Override
        public Object accept(ExpressionVisitor visitor, Object extraData) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }

    private class ValueExp extends ExpressionAbstract {

        private final int value;

        public ValueExp(int value) {
            this.value = value;
        }

        @Override
        public Object evaluate(Object object) {
            return Integer.valueOf(this.value);
        }

        @Override
        public Object accept(ExpressionVisitor visitor, Object extraData) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }

    private class OpacityExp extends ExpressionAbstract {

        private final double opacity;

        public OpacityExp(double opacity) {
            assert opacity <= 1.0 && opacity > 0.0;
            this.opacity = opacity;
        }

        @Override
        public Object evaluate(Object object) {
            return new Double(this.opacity);
        }

        @Override
        public Object accept(ExpressionVisitor visitor, Object extraData) {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }
 }
